﻿
#include "Meshfix/meshfix.h"

#include "Kernel/point.h"

#include "Kernel/orientation.h"

using namespace T_MESH;

double closestPair(List *bl1, List *bl2, Vertex **closest_on_bl1, Vertex **closest_on_bl2)
{
	Node *n, *m;
	Vertex *v, *w;
	double adist, mindist = DBL_MAX;

	FOREACHVVVERTEX(bl1, v, n)
		FOREACHVVVERTEX(bl2, w, m)
	if ((adist = w->squaredDistance(v))<mindist)
	{
		mindist = adist;
		*closest_on_bl1 = v;
		*closest_on_bl2 = w;
	}

	return mindist;
}

bool joinClosestComponents(Basic_TMesh *tin)
{
	Vertex *v, *w, *gv, *gw;
	Triangle *t, *s;
	Node *n;
	List triList, boundary_loops, *one_loop;
	List **bloops_array;
	int i, j, numloops;

	// Mark triangles with connected component's unique ID 
	i = 0;
	FOREACHVTTRIANGLE((&(tin->T)), t, n) t->info = NULL;
	FOREACHVTTRIANGLE((&(tin->T)), t, n) if (t->info == NULL)
	{
		i++;
		triList.appendHead(t);
		t->info = (void *)i;

		while (triList.numels())
		{
			t = (Triangle *)triList.popHead();
			if ((s = t->t1()) != NULL && s->info == NULL) { triList.appendHead(s); s->info = (void *)i; }
			if ((s = t->t2()) != NULL && s->info == NULL) { triList.appendHead(s); s->info = (void *)i; }
			if ((s = t->t3()) != NULL && s->info == NULL) { triList.appendHead(s); s->info = (void *)i; }
		}
	}

	if (i<2)
	{
		FOREACHVTTRIANGLE((&(tin->T)), t, n) t->info = NULL;
		//   JMesh::info("Mesh is a single component. Nothing done.");
		return false;
	}

	FOREACHVTTRIANGLE((&(tin->T)), t, n)
	{
		t->v1()->info = t->v2()->info = t->v3()->info = t->info;
	}

	FOREACHVVVERTEX((&(tin->V)), v, n) if (!IS_VISITED2(v) && v->isOnBoundary())
	{
		w = v;
		one_loop = new List;
		do
		{
			one_loop->appendHead(w); MARK_VISIT2(w);
			w = w->nextOnBoundary();
		} while (w != v);
		boundary_loops.appendHead(one_loop);
	}
	FOREACHVVVERTEX((&(tin->V)), v, n) UNMARK_VISIT2(v);

	bloops_array = (List **)boundary_loops.toArray();
	numloops = boundary_loops.numels();

	int numtris = tin->T.numels();
	double adist, mindist = DBL_MAX;

	gv = NULL;
	for (i = 0; i<numloops; i++)
	for (j = 0; j<numloops; j++)
	if (((Vertex *)bloops_array[i]->head()->data)->info != ((Vertex *)bloops_array[j]->head()->data)->info)
	{
		adist = closestPair(bloops_array[i], bloops_array[j], &v, &w);
		if (adist<mindist) { mindist = adist; gv = v; gw = w; }
	}

	if (gv != NULL) tin->joinBoundaryLoops(gv, gw, 1, 0);

	FOREACHVTTRIANGLE((&(tin->T)), t, n) t->info = NULL;
	FOREACHVVVERTEX((&(tin->V)), v, n) v->info = NULL;

	free(bloops_array);
	while ((one_loop = (List *)boundary_loops.popHead()) != NULL) delete one_loop;

	return (gv != NULL);
}

void usage()
{
 printf("\nMeshFix V2.0 - by Marco Attene\n------\n");
 printf("Usage: MeshFix inmeshfile [outmeshfile] [-a] [-j] [-x]\n");
 printf("  Processes 'inmeshfile' and saves the result to 'outmeshfile'\n");
 printf("  If 'outmeshfile' is not specified 'inmeshfile_fixed.off' will be produced\n");
 printf("  Option '-a' = joins multiple open components before starting\n");
 printf("  Option '-j' = output files in STL format insted of OFF\n");
 printf("  Option '-x' exits if output file already exists.\n");
 printf("  Accepted input formats are OFF, PLY and STL.\n  Other formats are supported only partially.\n");
 printf("\nIf MeshFix is used for research purposes, please cite the following paper:\n");
 printf("\n   M. Attene.\n   A lightweight approach to repairing digitized polygon meshes.\n   The Visual Computer, 2010. (c) Springer.\n");
 printf("\nHIT ENTER TO EXIT.\n");
 getchar();
 exit(0);
}

/**
* @brief 创建文件输入的名字
* @param iname 输入的文件名
* @param subext 分割符
* @param oname 输出的名字
* @param nameextension 新的延长词（用于做“后缀”）
* @return 最终产生的名字
*/
char *createFilename(const char *iname, const char *subext, char *oname, const char *newextension)
{
	static char tname[2048];
	strcpy(tname, iname);
	for (int n = strlen(tname) - 1; n>0; n--) if (tname[n] == '.') { tname[n] = '\0'; break; }
	sprintf(oname, "%s%s%s", tname, subext, newextension);
	return oname;
}

#ifndef MODEL_TRIANGLE3D
int main(int argc, char *argv[])
{

    // double * a = new double();
    // orient2d(a,a,a);
 //这是强制性的
 TMesh::init(); // This is mandatory
 TMesh::app_name = (char*)"MeshFix";
 TMesh::app_version = (char*)"2.0";
 TMesh::app_year = (char*)"2016";
 TMesh::app_authors = (char*)"Marco Attene";
 TMesh::app_maillist = (char*)"attene@ge.imati.cnr.it";

 clock_t beginning = clock();
 // Uncomment the following to prevent message reporting
 // TMesh::quiet = true;

 Basic_TMesh tin;
 bool stl_output = false;
 bool skip_if_fixed = false;
 bool join_multiple_components = false;
 char infilename[2048], outfilename[2048], extension[] = ".off";

 if (argc < 2) usage();

 float par;
 int i = 2;
 if (argc > 2 && argv[2][0] == '-') i--;

 for (; i<argc; i++)
 {
  if (i<argc-1) par = (float)atof(argv[i+1]); else par = 0;
  if (!strcmp(argv[i], "-x")) skip_if_fixed = true;
  else if (!strcmp(argv[i], "-a")) join_multiple_components = true;
  else if (!strcmp(argv[i], "-j")) stl_output = true;
  else if (argv[i][0] == '-') TMesh::warning("%s - Unknown operation.\n", argv[i]);

  if (par) i++;
 }

 sprintf(infilename, "%s", argv[1]);
 if (stl_output) strcpy(extension, ".stl");
 if (argc>2 && argv[2][0] != '-') sprintf(outfilename, "%s", argv[2]);
 else createFilename(infilename, "_fixed", outfilename, extension);

 if (skip_if_fixed && fopen(outfilename, "r")) TMesh::error("Output file already exists (-x option specified).");

 // The loader automatically reconstructs a manifold triangle connectivity
 //加载程序自动重新构造一个多三角形连接
 if (tin.load(infilename) != 0) TMesh::error("Can't open file.\n");

 if (join_multiple_components)
 {
	 TMesh::info("\nJoining input components ...\n");
	 TMesh::begin_progress();
	 while (joinClosestComponents(&tin)) TMesh::report_progress("Num. components: %d       ", tin.shells());
	 TMesh::end_progress();
	 tin.deselectTriangles();
 }

	   // Keep only the largest component (i.e. with most triangles)
	   int sc = tin.removeSmallestComponents();
	   if (sc) TMesh::warning("Removed %d small components\n",sc);

	   // Fill holes
	   if (tin.boundaries())
	   {
		TMesh::warning("Patching holes\n");
		tin.fillSmallBoundaries(0, true);
	   }

	   // Run geometry correction
	   if (!tin.boundaries()) TMesh::warning("Fixing degeneracies and intersections...\n");
	   if (tin.boundaries() || !tin.meshclean()) TMesh::warning("MeshFix could not fix everything.\n", sc);


 TMesh::info("Saving output mesh ...\n");
 tin.save(outfilename);

 printf("Elapsed time: %d ms\n", clock() - beginning);

 return 0;
}
#else
int saveSTL(const std::vector<Triangle3D> &triList,const char *fname)
{
    FILE *fp;
    char triname[256];
    Node *n;
    Triangle *t;
    Point nor;

    strcpy(triname, fname);

    if ((fp = fopen(triname, "w")) == NULL)
    {
        TMesh::warning("Can't open '%s' for output !\n", triname);
        return 1;
    }

    fprintf(fp, "solid T_MESH\n");

    for(unsigned int idx = 0;idx < triList.size();idx++)
    {
        Triangle3D *tr = (Triangle3D*)&triList[idx];
        fprintf(fp, " facet normal %f %f %d\n", TMESH_TO_FLOAT(tr->normal.x())*1000, TMESH_TO_FLOAT(tr->normal.y())*1000, TMESH_TO_FLOAT(tr->normal.z())*1000);
        fprintf(fp, "  outer loop\n");
        fprintf(fp, "   vertex %f %f %d\n", TMESH_TO_FLOAT(tr->vertex[0].x())*1000, TMESH_TO_FLOAT(tr->vertex[0].y())*1000, TMESH_TO_FLOAT(tr->vertex[0].z())*1000);
        fprintf(fp, "   vertex %f %f %d\n", TMESH_TO_FLOAT(tr->vertex[1].x())*1000, TMESH_TO_FLOAT(tr->vertex[1].y())*1000, TMESH_TO_FLOAT(tr->vertex[1].z())*1000);
        fprintf(fp, "   vertex %f %f %d\n", TMESH_TO_FLOAT(tr->vertex[2].x())*1000, TMESH_TO_FLOAT(tr->vertex[2].y())*1000, TMESH_TO_FLOAT(tr->vertex[2].z())*1000);
        fprintf(fp, "  endloop\n");
        fprintf(fp, " endfacet\n");
    }

    fprintf(fp, "endsolid T_MESH\n");

    fclose(fp);

    return 0;
}

std::vector<Triangle3D>* meshfix(const std::vector<Triangle3D> *triList,bool skip ,bool join,bool output)
{

    if(triList->empty()){
        printf("Model is Empty! \n");
        return (std::vector<Triangle3D> *)triList;
    }
    //这是强制性的
    TMesh::init(); // This is mandatory
    TMesh::app_name = (char*)"MeshFix";
    TMesh::app_version = (char*)"2.0";
    TMesh::app_year = (char*)"2016";
    TMesh::app_authors = (char*)"Marco Attene";
    TMesh::app_maillist = (char*)"attene@ge.imati.cnr.it";
    clock_t beginning = clock();

    // 取消注释，以防止消息报告
    // TMesh::quiet = true;

    Basic_TMesh tin;
    bool stl_output = output;
    bool skip_if_fixed = skip;//跳过如果固定
    bool join_multiple_components = join;//加入多个组件
    char infilename[2048], outfilename[2048], extension[] = ".off";
    sprintf(outfilename, "%s", "argv[2].stl");

    //sprintf(infilename, "%s", argv[1]); 给输入stl赋值我们不需要
    if (stl_output) strcpy(extension, ".stl");
    //if (argc > 2 && argv[2][0] != '-') sprintf(outfilename, "%s", argv[2]); 输出的名字 我们不需要
    //else createFilename(infilename, "_fixed", outfilename, extension);

    //! 判断能不能打开
    //if (skip_if_fixed && fopen(outfilename, "r")) TMesh::error("Output file already exists (-x option specified).");

    // The loader automatically reconstructs a manifold triangle connectivity
    //加载程序自动重新构造一个多三角形连接
    //if (tin.load(infilename) != 0) TMesh::error("Can't open file.\n");
    if (!tin.loadTr(*triList))TMesh::error("Can't open file.\n");
    if (join_multiple_components)
    {
        TMesh::info("\nJoining input components ...\n");
        TMesh::begin_progress();
        while (joinClosestComponents(&tin)) TMesh::report_progress("Num. components: %d       ", tin.shells());
        TMesh::end_progress();
        tin.deselectTriangles();
    }
	TMesh::warning("shells:%d\n", tin.shells());
	TMesh::warning("handles:%d\n", tin.handles());
	TMesh::warning("boundaries:%d\n", tin.boundaries());
	std::vector<Triangle3D>* result = new std::vector<Triangle3D>;
	auto count = tin.shells();
	for (size_t idx = 0; idx < count; idx++)
	{

	
		auto tin_part = tin.split();
		TMesh::warning("\n\n\n---------------index:%d------------------\n",idx);
		TMesh::warning("area:%f\n", tin_part->area());
		TMesh::warning("Triangle3D:%d\n", tin_part->T.numels());
		TMesh::warning("shells:%d\n", tin_part->shells());
		TMesh::warning("handles:%d\n", tin_part->handles());
		TMesh::warning("boundaries:%d\n", tin_part->boundaries());
		///TODO:理论上一个正常的3d模型应该至少拥有3个以上的面
		if (tin_part->T.numels() < 3)
		{
			continue;
		}
			// 只保留最大的组件(即大多数三角形)
			// 我们不需要删除部件，我们只修复
		#if 1
				int sc = tin_part->removeSmallestComponents();
				if (sc) TMesh::warning("Removed %d small components\n", sc);
		#else
			int sc = 0;
		#endif

		// 缺口填补
		if (tin_part->boundaries())
		{
			TMesh::warning("Patching holes\n");
			tin_part->fillSmallBoundaries(0, true);
		}

		// 进行几何校正
		if (!tin_part->boundaries()) TMesh::warning("Fixing degeneracies and intersections...\n");
		if (tin_part->boundaries() || !tin_part->meshclean()) TMesh::warning("MeshFix could not fix everything.\n", sc);


		TMesh::info("Saving output mesh ...\n");

		std::vector<Triangle3D> *result_part = new std::vector<Triangle3D>;
		tin_part->saveTr(*result_part);
		result->insert(result->end(), result_part->begin(), result_part->end());
	}
    //tin.save("two.stl");
    //saveSTL(*result,"twosilly.stl");
    printf("Elapsed time: %d ms\n", clock() - beginning);

    TMesh::report_progress("finish\n");

    return result;
}

#endif
